package com.allen.imsystem.user.service.impl;

import com.allen.imsystem.common.Const.GlobalConst;
import com.allen.imsystem.common.cache.CacheExecutor;
import com.allen.imsystem.common.cache.impl.UserCache;
import com.allen.imsystem.common.exception.BusinessException;
import com.allen.imsystem.common.exception.ExceptionType;
import com.allen.imsystem.user.mappers.UserInfoMapper;
import com.allen.imsystem.user.utils.HashSaltUtil;
import com.allen.imsystem.user.utils.JWTUtil;
import com.allen.imsystem.user.mappers.UserMapper;
import com.allen.imsystem.user.model.pojo.User;
import com.allen.imsystem.user.model.pojo.UserInfo;
import com.allen.imsystem.file.service.FileService;
import com.allen.imsystem.friend.service.FriendGroupService;
import com.allen.imsystem.user.service.UserService;
import com.allen.imsystem.id.IdPoolService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

import static com.allen.imsystem.common.Const.GlobalConst.*;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private FriendGroupService friendGroupService;

    @Autowired
    private FileService fileService;

    @Autowired
    private IdPoolService idPoolService;

    @Autowired
    private UserCache userCache;

    @Override
    public boolean isEmailRegisted(String email) {
        if (StringUtils.isEmpty(email) || !email.matches(GlobalConst.RegExp.EMAIL)) {
            throw new BusinessException(ExceptionType.PARAMETER_ILLEGAL, "邮箱格式不合法");
        }
        return this.lambdaQuery().eq(User::getEmail, email).count() > 0;
    }

    @Override
    @Transactional
    public void regist(String email, String password, String username) {
        /*
         * 从uid池里获取一个未使用的uid
         */
        String uid = idPoolService.nextId(IdType.UID);
        //对密码加密
        String salt = UUID.randomUUID().toString();
        password = HashSaltUtil.getHashSaltPwd(password, salt);

        // 插入数据库
        User user = new User(uid, password, salt, email);
        UserInfo userInfo = new UserInfo(uid, username);
        Random random = new Random(System.currentTimeMillis());
        int defaultAvatarName = random.nextInt(8) + 1;
        userInfo.setAvatar("default/" + defaultAvatarName + ".webp");
        this.save(user);
        userInfoMapper.insert(userInfo);
//        userMapper.insertLoginRecord(uid, new Date());
        // 为新用户创建一个默认分组
        friendGroupService.addFriendGroup(uid, "我的好友", true);
    }

    /**
     * 账号密码登录， 成功返回token，失败抛出业务异常
     *
     * @param password
     * @return
     */
    @Override
    public Map<String, Object> login(String uidOrEmail, String password) {
        User user = null;
        // 判断是email还是uid
        if (uidOrEmail.contains("@")) {
            user = userMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getEmail, uidOrEmail));
        } else {
            user = userMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getUid, uidOrEmail));
        }

        if (null == user)
            throw new BusinessException(ExceptionType.USERNAME_PASSWORD_ERROR);
        String hashPassword = user.getPassword();
        String salt = user.getSalt();
        if (!hashPassword.equals(HashSaltUtil.getHashSaltPwd(password, salt)))
            throw new BusinessException(ExceptionType.USERNAME_PASSWORD_ERROR);

        // 更新最后一次登录时间
//        userMapper.updateLoginRecord(user.getUid(), new Date());
        // redis更新该用户的在线状态 至在线
        CacheExecutor.set(userCache.userOnlineStatusCache, user.getUid(), GlobalConst.UserStatus.ONLINE);
        /**
         * 颁发token
         */
        String newToken = JWTUtil.createLoginToken(user.getUid());
        Map<String, Object> map = new HashMap<>(2);
        map.put("newToken", newToken);
        map.put("user", user);
        return map;
    }


    @Override
    public void logout(String uid) {
        CacheExecutor.set(userCache.userOnlineStatusCache, uid, UserStatus.OFFLINE);
    }

    @Override
    public Integer getUserOnlineStatus(String uid) {
        return CacheExecutor.get(userCache.userOnlineStatusCache,uid);
    }

    @Override
    public boolean isOnline(String uid) {
        if (uid == null) return false;
        Integer onlineStatus = getUserOnlineStatus(uid);
        return !onlineStatus.equals(0);
    }

    @Override
    public void forgetPassword(String email, String newPassword) {
        User user = this.lambdaQuery().eq(User::getEmail, email).one();
        if (user == null) {
            throw new BusinessException(ExceptionType.USER_NOT_FOUND);
        }
        // 密码检验并更新
        updatePassword(user, newPassword);
    }

    @Override
    public String modifyPassword(String uid, String oldPassword, String newPassword) {
        User user = this.getById(uid);
        if (user == null) {
            throw new BusinessException(ExceptionType.USER_NOT_FOUND);
        }
        if (!user.getPassword().equals(HashSaltUtil.getHashSaltPwd(oldPassword, user.getSalt()))) {
            throw new BusinessException(ExceptionType.PERMISSION_DENIED, "旧密码错误");
        }
        // 密码检验并更新
        updatePassword(user, newPassword);
        // 生成新token
        return JWTUtil.createLoginToken(user.getUid());
    }


    public void updatePassword(User user, String newPassword) {
        // 密码检验
        if (StringUtils.isEmpty(newPassword.trim()) || newPassword.length() < 6 || newPassword.length() > 12) {
            throw new BusinessException(ExceptionType.PARAMETER_ILLEGAL, "新密码长度应在6-12之间");
        }
        String salt = UUID.randomUUID().toString();
        newPassword = HashSaltUtil.getHashSaltPwd(newPassword, salt);
        user.setPassword(newPassword);
        user.setSalt(salt);
        user.setUpdateTime(new Date());
        this.updateById(user);
    }
}
